#!/usr/bin/python3 -s # -*- coding: utf-8 -*- # # Copyright (C) 2010-2016 Red Hat, Inc. # Authors: # Thomas Woerner # # 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 2 of the License, or # (at your option) 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 . # # python fork magic derived from setroubleshoot # Copyright (C) 2006,2007,2008,2009 Red Hat, Inc. # Authors: # John Dennis # Dan Walsh import os import sys import dbus import argparse from firewall import config from firewall.functions import firewalld_is_active from firewall.core.logger import log, FileLog def parse_cmdline(): parser = argparse.ArgumentParser() parser.add_argument('--debug', nargs='?', const=1, default=0, type=int, choices=range(1, log.DEBUG_MAX+1), help="""Enable logging of debug messages. Additional argument in range 1..%s can be used to specify log level.""" % log.DEBUG_MAX, metavar="level") parser.add_argument('--debug-gc', help="""Turn on garbage collector leak information. The collector runs every 10 seconds and if there are leaks, it prints information about the leaks.""", action="store_true") parser.add_argument('--nofork', help="""Turn off daemon forking, run as a foreground process.""", action="store_true") parser.add_argument('--nopid', help="""Disable writing pid file and don't check for existing server process.""", action="store_true") parser.add_argument('--system-config', help="""Path to firewalld system configuration""", metavar="path") parser.add_argument('--default-config', help="""Path to firewalld default configuration""", metavar="path") parser.add_argument('--log-target', choices=['mixed', 'syslog', 'file', 'console'], default='mixed', help="""Log target. mixed is a backward compatible mode logging to multiple targets. The modes syslog, file or console log to one target only.""") parser.add_argument('--log-file', help="""Path to firewalld log file""", metavar="path") return parser.parse_args() def setup_logging(args): # Set up logging capabilities if config.FIREWALLD_LOGTARGET == 'syslog': log.setFormat("%(label)s%(message)s") log.setInfoLogging("*", log.syslog) if args.debug: log.setDebugLogging("*", log.syslog) log.setInfoLogLevel(log.INFO_MAX) log.setDebugLogLevel(args.debug) elif config.FIREWALLD_LOGTARGET == 'file': log.setDateFormat("%Y-%m-%d %H:%M:%S") log.setFormat("%(date)s %(label)s%(message)s") log_file = FileLog(config.FIREWALLD_LOGFILE, "a") try: log_file.open() except IOError as e: print("Failed to open log file '%s': %s", config.FIREWALLD_LOGFILE, str(e)) else: log.setInfoLogging("*", log_file) if args.debug: log.setDebugLogging("*", log_file) log.setInfoLogLevel(log.INFO_MAX) log.setDebugLogLevel(args.debug) elif config.FIREWALLD_LOGTARGET == 'console': log.setDateFormat("%Y-%m-%d %H:%M:%S") log.setFormat("%(date)s %(label)s%(message)s") log.setInfoLogging("*", log.stdout) if args.debug: log.setDebugLogging("*", log.stdout) log.setInfoLogLevel(log.INFO_MAX) log.setDebugLogLevel(args.debug) else: log.setDateFormat("%Y-%m-%d %H:%M:%S") log.setFormat("%(date)s %(label)s%(message)s") log.setInfoLogging("*", log.syslog, [ log.FATAL, log.ERROR, log.WARNING, log.TRACEBACK ], fmt="%(label)s%(message)s") log.setDebugLogLevel(log.NO_INFO) log.setDebugLogLevel(log.NO_DEBUG) if args.debug: log.setInfoLogLevel(log.INFO_MAX) log.setDebugLogLevel(args.debug) if args.nofork: log.addInfoLogging("*", log.stdout) log.addDebugLogging("*", log.stdout) log_file = FileLog(config.FIREWALLD_LOGFILE, "a") try: log_file.open() except IOError as e: log.error("Failed to open log file '%s': %s", config.FIREWALLD_LOGFILE, str(e)) else: log.addInfoLogging("*", log_file, [ log.FATAL, log.ERROR, log.WARNING, log.TRACEBACK ]) log.addDebugLogging("*", log_file) if args.debug: log.addInfoLogging("*", log_file) log.addDebugLogging("*", log_file) def startup(args): try: if not args.nofork: # do the UNIX double-fork magic, see Stevens' "Advanced # Programming in the UNIX Environment" for details (ISBN 0201563177) pid = os.fork() if pid > 0: # exit first parent sys.exit(0) # decouple from parent environment os.chdir("/") os.setsid() os.umask(os.umask(0o077) | 0o022) # Do not close the file descriptors here anymore # File descriptors are now closed in runProg before execve # Redirect the standard I/O file descriptors to /dev/null if hasattr(os, "devnull"): REDIRECT_TO = os.devnull else: REDIRECT_TO = "/dev/null" fd = os.open(REDIRECT_TO, os.O_RDWR) os.dup2(fd, 0) # standard input (0) os.dup2(fd, 1) # standard output (1) os.dup2(fd, 2) # standard error (2) if not args.nopid: # write the pid file with open(config.FIREWALLD_PIDFILE, "w") as f: f.write(str(os.getpid())) if not os.path.exists(config.FIREWALLD_TEMPDIR): os.mkdir(config.FIREWALLD_TEMPDIR, 0o750) # attempt to drop Linux capabilities to a minimal set: # - CAP_NET_ADMIN # - CAP_NET_RAW # - CAP_SYS_MODULE try: import capng capng.capng_clear(capng.CAPNG_SELECT_BOTH) if capng.capng_update(capng.CAPNG_ADD, capng.CAPNG_EFFECTIVE | capng.CAPNG_PERMITTED | capng.CAPNG_BOUNDING_SET, capng.CAP_NET_ADMIN) or \ capng.capng_update(capng.CAPNG_ADD, capng.CAPNG_EFFECTIVE | capng.CAPNG_PERMITTED | capng.CAPNG_BOUNDING_SET, capng.CAP_NET_RAW) or \ capng.capng_update(capng.CAPNG_ADD, capng.CAPNG_EFFECTIVE | capng.CAPNG_PERMITTED | capng.CAPNG_BOUNDING_SET, capng.CAP_SYS_MODULE) or \ capng.capng_apply(capng.CAPNG_SELECT_BOTH): log.info(log.INFO1, "libcap-ng failed to drop Linux capabilities.") else: log.info(log.INFO1, "Dropped Linux capabilities to NET_ADMIN, NET_RAW, SYS_MODULE.") except ImportError: pass if args.system_config: config.set_system_config_paths(args.system_config) if args.default_config: config.set_default_config_paths(args.default_config) # Start the server mainloop here from firewall.server import server server.run_server(args.debug_gc) # Clean up on exit if not args.nopid and os.path.exists(config.FIREWALLD_PIDFILE): os.remove(config.FIREWALLD_PIDFILE) except OSError as e: log.fatal("Fork #1 failed: %d (%s)" % (e.errno, e.strerror)) log.exception() if not args.nopid and os.path.exists(config.FIREWALLD_PIDFILE): os.remove(config.FIREWALLD_PIDFILE) sys.exit(1) except dbus.exceptions.DBusException as e: log.fatal(str(e)) log.exception() if not args.nopid and os.path.exists(config.FIREWALLD_PIDFILE): os.remove(config.FIREWALLD_PIDFILE) sys.exit(1) except IOError as e: log.fatal(str(e)) log.exception() if not args.nopid and os.path.exists(config.FIREWALLD_PIDFILE): os.remove(config.FIREWALLD_PIDFILE) sys.exit(1) def main(): # firewalld should only be run as the root user if os.getuid() != 0: print("You need to be root to run %s." % sys.argv[0]) sys.exit(-1) # Process the command-line arguments args = parse_cmdline() config.FIREWALLD_LOGTARGET = args.log_target if args.log_file: config.FIREWALLD_LOGFILE = args.log_file setup_logging(args) # Don't attempt to run two copies of firewalld simultaneously if not args.nopid and firewalld_is_active(): log.fatal("Not starting FirewallD, already running.") sys.exit(1) startup(args) sys.exit(0) if __name__ == '__main__': main()