mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 15:12:02 +00:00
Fix clickhouse-su building in splitted build
- Add status log message - Add it to clickhouse-bundle in shared build - Move clickhouse-su.cpp into su.cpp, since executable does not have include directories of linked libraries (dbms here), only clickhouse-lib-su does, hence it cannot find includes CI: https://github.com/ClickHouse/ClickHouse/runs/7566319416?check_suite_focus=true Signed-off-by: Azat Khuzhin <a.khuzhin@semrush.com>
This commit is contained in:
parent
4b0594633b
commit
b90152b6ec
@ -195,6 +195,12 @@ else()
|
||||
message(STATUS "ClickHouse disks mode: OFF")
|
||||
endif()
|
||||
|
||||
if (ENABLE_CLICKHOUSE_SU)
|
||||
message(STATUS "ClickHouse su: ON")
|
||||
else()
|
||||
message(STATUS "ClickHouse su: OFF")
|
||||
endif()
|
||||
|
||||
configure_file (config_tools.h.in ${ConfigIncludePath}/config_tools.h)
|
||||
|
||||
macro(clickhouse_target_link_split_lib target name)
|
||||
@ -367,6 +373,10 @@ if (CLICKHOUSE_SPLIT_BINARY)
|
||||
list (APPEND CLICKHOUSE_ALL_TARGETS clickhouse-keeper-converter)
|
||||
endif ()
|
||||
|
||||
if (ENABLE_CLICKHOUSE_SU)
|
||||
list (APPEND CLICKHOUSE_ALL_TARGETS clickhouse-su)
|
||||
endif ()
|
||||
|
||||
set_target_properties(${CLICKHOUSE_ALL_TARGETS} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ..)
|
||||
|
||||
add_custom_target (clickhouse-bundle ALL DEPENDS ${CLICKHOUSE_ALL_TARGETS})
|
||||
|
@ -1,3 +1,3 @@
|
||||
set (CLICKHOUSE_SU_SOURCES clickhouse-su.cpp)
|
||||
set (CLICKHOUSE_SU_SOURCES clickhouse-su.cpp su.cpp)
|
||||
set (CLICKHOUSE_SU_LINK PRIVATE dbms)
|
||||
clickhouse_program_add(su)
|
||||
|
@ -1,145 +1,2 @@
|
||||
#include <Common/Exception.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <fmt/format.h>
|
||||
#include <vector>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
|
||||
/// "su" means "set user"
|
||||
/// In fact, this program can set Unix user and group.
|
||||
///
|
||||
/// Usage:
|
||||
/// clickhouse su user[:group] args...
|
||||
///
|
||||
/// - will set user and, optionally, group and exec the remaining args.
|
||||
/// user and group can be numeric identifiers or strings.
|
||||
///
|
||||
/// The motivation for this tool is very obscure and idiosyncratic. It is needed for Docker.
|
||||
/// People want to run programs inside Docker with dropped privileges (less than root).
|
||||
/// But the standard Linux "su" program is not suitable for usage inside Docker,
|
||||
/// because it is creating pseudoterminals to avoid hijacking input from the terminal, for security,
|
||||
/// but Docker is also doing something with the terminal and it is incompatible.
|
||||
/// For this reason, people use alternative and less "secure" versions of "su" tools like "gosu" or "su-exec".
|
||||
/// But it would be very strange to use 3rd-party software only to do two-three syscalls.
|
||||
/// That's why we provide this tool.
|
||||
///
|
||||
/// Note: ClickHouse does not need Docker at all and works better without Docker.
|
||||
/// ClickHouse has no dependencies, it is packaged and distributed in single binary.
|
||||
/// There is no reason to use Docker unless you are already running all your software in Docker.
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int BAD_ARGUMENTS;
|
||||
extern const int SYSTEM_ERROR;
|
||||
}
|
||||
|
||||
void setUserAndGroup(std::string arg_uid, std::string arg_gid)
|
||||
{
|
||||
static constexpr size_t buf_size = 16384; /// Linux man page says it is enough. Nevertheless, we will check if it's not enough and throw.
|
||||
std::unique_ptr<char[]> buf(new char[buf_size]);
|
||||
|
||||
/// Set the group first, because if we set user, the privileges will be already dropped and we will not be able to set the group later.
|
||||
|
||||
if (!arg_gid.empty())
|
||||
{
|
||||
gid_t gid = 0;
|
||||
if (!tryParse(gid, arg_gid) || gid == 0)
|
||||
{
|
||||
group entry{};
|
||||
group * result{};
|
||||
|
||||
if (0 != getgrnam_r(arg_gid.data(), &entry, buf.get(), buf_size, &result))
|
||||
throwFromErrno(fmt::format("Cannot do 'getgrnam_r' to obtain gid from group name ({})", arg_gid), ErrorCodes::SYSTEM_ERROR);
|
||||
|
||||
if (!result)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Group {} is not found in the system", arg_gid);
|
||||
|
||||
gid = entry.gr_gid;
|
||||
}
|
||||
|
||||
if (gid == 0 && getgid() != 0)
|
||||
throw Exception("Group has id 0, but dropping privileges to gid 0 does not make sense", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
if (0 != setgid(gid))
|
||||
throwFromErrno(fmt::format("Cannot do 'setgid' to user ({})", arg_gid), ErrorCodes::SYSTEM_ERROR);
|
||||
}
|
||||
|
||||
if (!arg_uid.empty())
|
||||
{
|
||||
/// Is it numeric id or name?
|
||||
uid_t uid = 0;
|
||||
if (!tryParse(uid, arg_uid) || uid == 0)
|
||||
{
|
||||
passwd entry{};
|
||||
passwd * result{};
|
||||
|
||||
if (0 != getpwnam_r(arg_uid.data(), &entry, buf.get(), buf_size, &result))
|
||||
throwFromErrno(fmt::format("Cannot do 'getpwnam_r' to obtain uid from user name ({})", arg_uid), ErrorCodes::SYSTEM_ERROR);
|
||||
|
||||
if (!result)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "User {} is not found in the system", arg_uid);
|
||||
|
||||
uid = entry.pw_uid;
|
||||
}
|
||||
|
||||
if (uid == 0 && getuid() != 0)
|
||||
throw Exception("User has id 0, but dropping privileges to uid 0 does not make sense", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
if (0 != setuid(uid))
|
||||
throwFromErrno(fmt::format("Cannot do 'setuid' to user ({})", arg_uid), ErrorCodes::SYSTEM_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
int mainEntryClickHouseSU(int argc, char ** argv)
|
||||
try
|
||||
{
|
||||
using namespace DB;
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
std::cout << "Usage: ./clickhouse su user:group ..." << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
std::string_view user_and_group = argv[1];
|
||||
|
||||
std::string user;
|
||||
std::string group;
|
||||
|
||||
auto pos = user_and_group.find(':');
|
||||
if (pos == std::string_view::npos)
|
||||
{
|
||||
user = user_and_group;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = user_and_group.substr(0, pos);
|
||||
group = user_and_group.substr(pos + 1);
|
||||
}
|
||||
|
||||
setUserAndGroup(std::move(user), std::move(group));
|
||||
|
||||
std::vector<char *> new_argv;
|
||||
new_argv.reserve(argc - 1);
|
||||
new_argv.insert(new_argv.begin(), argv + 2, argv + argc);
|
||||
new_argv.push_back(nullptr);
|
||||
|
||||
execvp(new_argv.front(), new_argv.data());
|
||||
|
||||
throwFromErrno("Cannot execvp", ErrorCodes::SYSTEM_ERROR);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << DB::getCurrentExceptionMessage(false) << '\n';
|
||||
return 1;
|
||||
}
|
||||
int mainEntryClickHouseSU(int argc, char ** argv);
|
||||
int main(int argc_, char ** argv_) { return mainEntryClickHouseSU(argc_, argv_); }
|
||||
|
145
programs/su/su.cpp
Normal file
145
programs/su/su.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include <Common/Exception.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <fmt/format.h>
|
||||
#include <vector>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
|
||||
/// "su" means "set user"
|
||||
/// In fact, this program can set Unix user and group.
|
||||
///
|
||||
/// Usage:
|
||||
/// clickhouse su user[:group] args...
|
||||
///
|
||||
/// - will set user and, optionally, group and exec the remaining args.
|
||||
/// user and group can be numeric identifiers or strings.
|
||||
///
|
||||
/// The motivation for this tool is very obscure and idiosyncratic. It is needed for Docker.
|
||||
/// People want to run programs inside Docker with dropped privileges (less than root).
|
||||
/// But the standard Linux "su" program is not suitable for usage inside Docker,
|
||||
/// because it is creating pseudoterminals to avoid hijacking input from the terminal, for security,
|
||||
/// but Docker is also doing something with the terminal and it is incompatible.
|
||||
/// For this reason, people use alternative and less "secure" versions of "su" tools like "gosu" or "su-exec".
|
||||
/// But it would be very strange to use 3rd-party software only to do two-three syscalls.
|
||||
/// That's why we provide this tool.
|
||||
///
|
||||
/// Note: ClickHouse does not need Docker at all and works better without Docker.
|
||||
/// ClickHouse has no dependencies, it is packaged and distributed in single binary.
|
||||
/// There is no reason to use Docker unless you are already running all your software in Docker.
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int BAD_ARGUMENTS;
|
||||
extern const int SYSTEM_ERROR;
|
||||
}
|
||||
|
||||
void setUserAndGroup(std::string arg_uid, std::string arg_gid)
|
||||
{
|
||||
static constexpr size_t buf_size = 16384; /// Linux man page says it is enough. Nevertheless, we will check if it's not enough and throw.
|
||||
std::unique_ptr<char[]> buf(new char[buf_size]);
|
||||
|
||||
/// Set the group first, because if we set user, the privileges will be already dropped and we will not be able to set the group later.
|
||||
|
||||
if (!arg_gid.empty())
|
||||
{
|
||||
gid_t gid = 0;
|
||||
if (!tryParse(gid, arg_gid) || gid == 0)
|
||||
{
|
||||
group entry{};
|
||||
group * result{};
|
||||
|
||||
if (0 != getgrnam_r(arg_gid.data(), &entry, buf.get(), buf_size, &result))
|
||||
throwFromErrno(fmt::format("Cannot do 'getgrnam_r' to obtain gid from group name ({})", arg_gid), ErrorCodes::SYSTEM_ERROR);
|
||||
|
||||
if (!result)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Group {} is not found in the system", arg_gid);
|
||||
|
||||
gid = entry.gr_gid;
|
||||
}
|
||||
|
||||
if (gid == 0 && getgid() != 0)
|
||||
throw Exception("Group has id 0, but dropping privileges to gid 0 does not make sense", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
if (0 != setgid(gid))
|
||||
throwFromErrno(fmt::format("Cannot do 'setgid' to user ({})", arg_gid), ErrorCodes::SYSTEM_ERROR);
|
||||
}
|
||||
|
||||
if (!arg_uid.empty())
|
||||
{
|
||||
/// Is it numeric id or name?
|
||||
uid_t uid = 0;
|
||||
if (!tryParse(uid, arg_uid) || uid == 0)
|
||||
{
|
||||
passwd entry{};
|
||||
passwd * result{};
|
||||
|
||||
if (0 != getpwnam_r(arg_uid.data(), &entry, buf.get(), buf_size, &result))
|
||||
throwFromErrno(fmt::format("Cannot do 'getpwnam_r' to obtain uid from user name ({})", arg_uid), ErrorCodes::SYSTEM_ERROR);
|
||||
|
||||
if (!result)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "User {} is not found in the system", arg_uid);
|
||||
|
||||
uid = entry.pw_uid;
|
||||
}
|
||||
|
||||
if (uid == 0 && getuid() != 0)
|
||||
throw Exception("User has id 0, but dropping privileges to uid 0 does not make sense", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
if (0 != setuid(uid))
|
||||
throwFromErrno(fmt::format("Cannot do 'setuid' to user ({})", arg_uid), ErrorCodes::SYSTEM_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
int mainEntryClickHouseSU(int argc, char ** argv)
|
||||
try
|
||||
{
|
||||
using namespace DB;
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
std::cout << "Usage: ./clickhouse su user:group ..." << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
std::string_view user_and_group = argv[1];
|
||||
|
||||
std::string user;
|
||||
std::string group;
|
||||
|
||||
auto pos = user_and_group.find(':');
|
||||
if (pos == std::string_view::npos)
|
||||
{
|
||||
user = user_and_group;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = user_and_group.substr(0, pos);
|
||||
group = user_and_group.substr(pos + 1);
|
||||
}
|
||||
|
||||
setUserAndGroup(std::move(user), std::move(group));
|
||||
|
||||
std::vector<char *> new_argv;
|
||||
new_argv.reserve(argc - 1);
|
||||
new_argv.insert(new_argv.begin(), argv + 2, argv + argc);
|
||||
new_argv.push_back(nullptr);
|
||||
|
||||
execvp(new_argv.front(), new_argv.data());
|
||||
|
||||
throwFromErrno("Cannot execvp", ErrorCodes::SYSTEM_ERROR);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << DB::getCurrentExceptionMessage(false) << '\n';
|
||||
return 1;
|
||||
}
|
Loading…
Reference in New Issue
Block a user