Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: replace root by a custom user with root privileges #1009

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
398 changes: 239 additions & 159 deletions crmsh/bootstrap.py

Large diffs are not rendered by default.

15 changes: 14 additions & 1 deletion crmsh/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import re
import configparser
from contextlib import contextmanager
from typing import List
from . import userdir


Expand Down Expand Up @@ -237,6 +238,7 @@ def get(self, value):
'editor': opt_program('EDITOR', ('vim', 'vi', 'emacs', 'nano')),
'pager': opt_program('PAGER', ('less', 'more', 'pg')),
'user': opt_string(''),
'hosts': opt_list([]), # 'alice@host1, bob@host2'
'skill_level': opt_choice('expert', ('operator', 'administrator', 'expert')),
'sort_elements': opt_boolean('yes'),
'check_frequency': opt_choice('always', ('always', 'on-verify', 'never')),
Expand Down Expand Up @@ -432,7 +434,18 @@ def save():


def set_option(section, option, value):
_configuration.set(section, option, value)
if not isinstance(value, List):
_configuration.set(section, option, value)
return
string = ""
first = True
for item in value:
if first:
first = False
else:
string += ", "
string += str(item)
_configuration.set(section, option, string)


def get_option(section, option, raw=False):
Expand Down
5 changes: 3 additions & 2 deletions crmsh/corosync.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,10 @@ def query_qnetd_status():
raise ValueError("host for qnetd not configured!")

# Configure ssh passwordless to qnetd if detect password is needed
if utils.check_ssh_passwd_need(qnetd_addr):
local_user = utils.getuser()
if utils.check_ssh_passwd_need(utils.getuser(), utils.user_of(qnetd_addr), qnetd_addr):
print("Copy ssh key to qnetd node({})".format(qnetd_addr))
rc, _, err = utils.get_stdout_stderr("ssh-copy-id -i /root/.ssh/id_rsa.pub root@{}".format(qnetd_addr))
rc, _, err = utils.get_stdout_stderr("ssh-copy-id -i ~{}/.ssh/id_rsa.pub root@{}".format(local_user, qnetd_addr))
if rc != 0:
raise ValueError(err)

Expand Down
9 changes: 5 additions & 4 deletions crmsh/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ class Lock(object):
A base class define a lock mechanism used to exclude other nodes
"""

LOCK_DIR_DEFAULT = "/run/.crmsh_lock_directory"
LOCK_DIR_NON_PRIVILEGED = "/tmp/.crmsh_lock_directory"

def __init__(self, lock_dir=None):
"""
Init function
"""
# only the lock owner can unlock
self.lock_owner = False
self.lock_dir = lock_dir or self.LOCK_DIR_DEFAULT
self.lock_dir = lock_dir or self.LOCK_DIR_NON_PRIVILEGED

def _run(self, cmd):
"""
Expand Down Expand Up @@ -100,10 +100,11 @@ class RemoteLock(Lock):
MIN_LOCK_TIMEOUT = 120
WAIT_INTERVAL = 10

def __init__(self, remote_node, for_join=True, lock_dir=None, wait=True, no_warn=False):
def __init__(self, remote_user, remote_node, for_join=True, lock_dir=None, wait=True, no_warn=False):
"""
Init function
"""
self.remote_user = remote_user
self.remote_node = remote_node
self.for_join = for_join
self.wait = wait
Expand All @@ -114,7 +115,7 @@ def _run(self, cmd):
"""
Run command on remote node
"""
cmd = "ssh {} root@{} \"{}\"".format(self.SSH_OPTION, self.remote_node, cmd)
cmd = "ssh {} {}@{} \"{}\"".format(self.SSH_OPTION, self.remote_user, self.remote_node, cmd)
rc, out, err = utils.get_stdout_stderr(cmd)
if rc == self.SSH_EXIT_ERR:
raise SSHError(err)
Expand Down
6 changes: 5 additions & 1 deletion crmsh/parallax.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ def handle(self, results):
return results

def call(self):
results = parallax.call(self.nodes, self.cmd, self.opts)
from crmsh.utils import user_of
host_port_user = []
for host in self.nodes:
host_port_user.append([host, None, user_of(host)])
results = parallax.call(host_port_user, self.cmd, self.opts)
return self.handle(list(results.items()))

def slurp(self):
Expand Down
10 changes: 6 additions & 4 deletions crmsh/qdevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ def qnetd_lock_for_same_cluster_name(func):
def wrapper(*args, **kwargs):
cluster_name = args[0].cluster_name
lock_dir = "/run/.crmsh_qdevice_lock_for_{}".format(cluster_name)
lock_inst = lock.RemoteLock(args[0].qnetd_addr, for_join=False, lock_dir=lock_dir, wait=False)
lock_inst = lock.RemoteLock(utils.user_of(args[0].qnetd_addr), args[0].qnetd_addr
, for_join=False, lock_dir=lock_dir, wait=False)
try:
with lock_inst.lock():
func(*args, **kwargs)
Expand All @@ -80,7 +81,8 @@ def qnetd_lock_for_multi_cluster(func):
"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
lock_inst = lock.RemoteLock(args[0].qnetd_addr, for_join=False, no_warn=True)
lock_inst = lock.RemoteLock(utils.user_of(args[0].qnetd_addr)
, args[0].qnetd_addr, for_join=False, no_warn=True)
try:
with lock_inst.lock():
func(*args, **kwargs)
Expand Down Expand Up @@ -285,10 +287,10 @@ def valid_qnetd(self):
exception_msg = ""
suggest = ""
duplicated_cluster_name = False
if not utils.package_is_installed("corosync-qnetd", self.qnetd_addr):
if not utils.package_is_installed("corosync-qnetd", remote_addr=self.qnetd_addr):
exception_msg = "Package \"corosync-qnetd\" not installed on {}!".format(self.qnetd_addr)
suggest = "install \"corosync-qnetd\" on {}".format(self.qnetd_addr)
elif utils.service_is_active("corosync-qnetd", self.qnetd_addr):
elif utils.service_is_active("corosync-qnetd", remote_addr=self.qnetd_addr):
cmd = "corosync-qnetd-tool -l -c {}".format(self.cluster_name)
if utils.get_stdout_or_raise_error(cmd, remote=self.qnetd_addr):
duplicated_cluster_name = True
Expand Down
22 changes: 11 additions & 11 deletions crmsh/sbd.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def get_sbd_msgwait(dev):
"""
Get msgwait for sbd device
"""
out = utils.get_stdout_or_raise_error("sbd -d {} dump".format(dev))
out = utils.get_stdout_or_raise_error("sudo sbd -d {} dump".format(dev))
# Format like "Timeout (msgwait) : 30"
res = re.search("\(msgwait\)\s+:\s+(\d+)", out)
if not res:
Expand Down Expand Up @@ -292,7 +292,7 @@ def _get_device_uuid(dev, node=None):
"""
Get UUID for specific device and node
"""
out = utils.get_stdout_or_raise_error("sbd -d {} dump".format(dev), remote=node)
out = utils.get_stdout_or_raise_error("sudo sbd -d {} dump".format(dev), remote=node)
res = re.search("UUID\s*:\s*(.*)\n", out)
if not res:
raise ValueError("Cannot find sbd device UUID for {}".format(dev))
Expand Down Expand Up @@ -419,7 +419,7 @@ def _initialize_sbd(self):
for dev in self._sbd_devices:
if dev in self.no_overwrite_map and self.no_overwrite_map[dev]:
continue
rc, _, err = bootstrap.invoke("sbd {} -d {} create".format(opt, dev))
rc, _, err = bootstrap.invoke("sudo sbd {} -d {} create".format(opt, dev))
if not rc:
utils.fatal("Failed to initialize SBD device {}: {}".format(dev, err))

Expand All @@ -431,7 +431,7 @@ def _update_sbd_configuration(self):
bootstrap.sync_file(SYSCONFIG_SBD)
return

shutil.copyfile(self.SYSCONFIG_SBD_TEMPLATE, SYSCONFIG_SBD)
utils.copy_local_file(self.SYSCONFIG_SBD_TEMPLATE, SYSCONFIG_SBD)
sbd_config_dict = {
"SBD_WATCHDOG_DEV": self._watchdog_inst.watchdog_device_name,
"SBD_WATCHDOG_TIMEOUT": str(self.timeout_inst.sbd_watchdog_timeout)
Expand Down Expand Up @@ -478,7 +478,7 @@ def _enable_sbd_service(self):
self._restart_cluster_and_configure_sbd_ra()
else:
# in init process
bootstrap.invoke("systemctl enable sbd.service")
bootstrap.invoke("sudo systemctl enable sbd.service")

def _warn_diskless_sbd(self, peer=None):
"""
Expand Down Expand Up @@ -509,7 +509,7 @@ def sbd_init(self):
self._watchdog_inst.init_watchdog()
self._get_sbd_device()
if not self._sbd_devices and not self.diskless_sbd:
bootstrap.invoke("systemctl disable sbd.service")
bootstrap.invoke("sudo systemctl disable sbd.service")
return
self._warn_diskless_sbd()
self._initialize_sbd()
Expand Down Expand Up @@ -541,7 +541,7 @@ def configure_sbd_resource_and_properties(self):
if self._context.cluster_is_running:
bootstrap.adjust_properties()

def join_sbd(self, peer_host):
def join_sbd(self, remote_user, peer_host):
"""
Function join_sbd running on join process only
On joining process, check whether peer node has enabled sbd.service
Expand All @@ -552,17 +552,17 @@ def join_sbd(self, peer_host):
if not utils.package_is_installed("sbd"):
return
if not os.path.exists(SYSCONFIG_SBD) or not utils.service_is_enabled("sbd.service", peer_host):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only thing changed in this line is coding style. Is there any consensus on it?

bootstrap.invoke("systemctl disable sbd.service")
bootstrap.invoke("sudo systemctl disable sbd.service")
return
self._watchdog_inst = Watchdog(peer_host=peer_host)
self._watchdog_inst = Watchdog(remote_user=remote_user, peer_host=peer_host)
self._watchdog_inst.join_watchdog()
dev_list = self._get_sbd_device_from_config()
if dev_list:
self._verify_sbd_device(dev_list, [peer_host])
else:
self._warn_diskless_sbd(peer_host)
logger.info("Got {}SBD configuration".format("" if dev_list else "diskless "))
bootstrap.invoke("systemctl enable sbd.service")
bootstrap.invoke("sudo systemctl enable sbd.service")

@classmethod
def verify_sbd_device(cls):
Expand Down Expand Up @@ -617,6 +617,6 @@ def has_sbd_device_already_initialized(dev):
"""
Check if sbd device already initialized
"""
cmd = "sbd -d {} dump".format(dev)
cmd = "sudo sbd -d {} dump".format(dev)
rc, _, _ = utils.get_stdout_stderr(cmd)
return rc == 0
11 changes: 8 additions & 3 deletions crmsh/ui_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def do_stop(self, context, *args):
if not node_list:
return

dc_deadtime = utils.get_property("dc-deadtime") or constants.DC_DEADTIME_DEFAULT
dc_deadtime = utils.get_property("dc-deadtime") or str(constants.DC_DEADTIME_DEFAULT)
dc_timeout = int(dc_deadtime.strip('s')) + 5
try:
utils.check_function_with_timeout(utils.get_dc, wait_timeout=dc_timeout)
Expand Down Expand Up @@ -719,11 +719,16 @@ def do_run(self, context, cmd, *nodes):
opts = parallax.Options()
opts.ssh_options = ['StrictHostKeyChecking=no']
for host in hosts:
res = utils.check_ssh_passwd_need(host)
res = utils.check_ssh_passwd_need(utils.getuser(), utils.user_of(host), host)
if res:
opts.askpass = True
break
for host, result in parallax.call(hosts, cmd, opts).items():

host_port_user = []
for host in hosts:
host_port_user.append([host, None, utils.user_of(host)])

for host, result in parallax.call(host_port_user, cmd, opts).items():
if isinstance(result, parallax.Error):
logger.error("[%s]: %s" % (host, result))
else:
Expand Down
10 changes: 4 additions & 6 deletions crmsh/upgradeutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,10 @@ def force_set_local_upgrade_seq():
"""Create the upgrade sequence file and set it to CURRENT_UPGRADE_SEQ.

It should only be used when initializing new cluster nodes."""
try:
os.mkdir(DATA_DIR)
except FileExistsError:
pass
with open(SEQ_FILE_PATH, 'w', encoding='ascii') as f:
print(_format_upgrade_seq(CURRENT_UPGRADE_SEQ), file=f)
if not os.path.exists(DATA_DIR):
crmsh.utils.mkdirs_owned(DATA_DIR, mode=0o755)
up_seq = _format_upgrade_seq(CURRENT_UPGRADE_SEQ)
crmsh.utils.str2file(up_seq, SEQ_FILE_PATH)


def main():
Expand Down
Loading