From caf7b583f87f6dbcfb7c7f4837ef4478d67d533c Mon Sep 17 00:00:00 2001 From: Sasha Romijn Date: Thu, 12 Jan 2023 21:22:58 +0100 Subject: [PATCH] Fix #666 - Fail explicitly when missing logfile permissions (#730) --- docs/releases/4.3.0.rst | 15 ++++++++++----- irrd/daemon/main.py | 26 +++++++++++++++++++++----- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/docs/releases/4.3.0.rst b/docs/releases/4.3.0.rst index 8eea6842b..f3019c739 100644 --- a/docs/releases/4.3.0.rst +++ b/docs/releases/4.3.0.rst @@ -117,8 +117,13 @@ recommended before upgrading, as it makes database migrations significantly faster. -Debugging info on SIGUSR1 -------------------------- -IRRd processes will now log a traceback of all their threads when -receiving a SIGUSR1 signal. This can be helpful when debugging -hanging workers or other complex issues. +Other changes +------------- +* IRRd processes will now log a traceback of all their threads when + receiving a SIGUSR1 signal. This can be helpful when debugging + hanging workers or other complex issues. +* When configured to drop privileges after starting, IRRd will now + check whether the less privileged user is able to write to the + log file, before dropping the privileges. Previously, it would + drop privileges, then fail to write to the log file, and be unable + to report this error. diff --git a/irrd/daemon/main.py b/irrd/daemon/main.py index 911f46f10..3459a39ea 100755 --- a/irrd/daemon/main.py +++ b/irrd/daemon/main.py @@ -59,15 +59,25 @@ def main(): # but this call here causes fast failure for most misconfigurations config_init(args.config_file_path, commit=False) + staged_logfile_path = get_configuration().user_config_staging.get('log.logfile_path') + staged_logging_config_path = get_configuration().user_config_staging.get('log.logging_config_path') if not any([ - get_configuration().user_config_staging.get('log.logfile_path'), - get_configuration().user_config_staging.get('log.logging_config_path'), + staged_logfile_path, + staged_logging_config_path, args.foreground, ]): logging.critical('Unable to start: when not running in the foreground, you must set ' 'either log.logfile_path or log.logging_config_path in the settings') return + uid, gid = get_configured_owner(from_staging=True) + if uid and gid: + os.setegid(gid) + os.seteuid(uid) + if staged_logfile_path and not os.access(staged_logfile_path, os.W_OK, effective_ids=True): + logging.critical(f'Unable to start: logfile {staged_logfile_path} not writable by UID {uid} / GID {gid}') + return + with daemon.DaemonContext(**daemon_kwargs): config_init(args.config_file_path) @@ -182,10 +192,16 @@ def sigterm_handler(signum, frame): logging.info(f'Main process exiting') -def get_configured_owner() -> Tuple[Optional[int], Optional[int]]: +def get_configured_owner(from_staging=False) -> Tuple[Optional[int], Optional[int]]: uid = gid = None - user = get_setting('user') - group = get_setting('group') + if not from_staging: + user = get_setting('user') + group = get_setting('group') + else: + config = get_configuration() + assert config + user = config.user_config_staging.get('user') + group = config.user_config_staging.get('group') if user and group: uid = pwd.getpwnam(user).pw_uid gid = grp.getgrnam(group).gr_gid