Пример #1
0
def test_listen_fds_default_unset():
    os.environ['LISTEN_FDS'] = '1'
    os.environ['LISTEN_PID'] = str(os.getpid())

    assert listen_fds(False) == [3]
    assert listen_fds() == [3]
    assert listen_fds() == []
Пример #2
0
def test_listen_fds_default_unset():
    os.environ["LISTEN_FDS"] = "1"
    os.environ["LISTEN_PID"] = str(os.getpid())

    assert listen_fds(False) == [3]
    assert listen_fds() == [3]
    assert listen_fds() == []
Пример #3
0
def test_listen_fds():
    os.environ['LISTEN_FDS'] = '3'
    os.environ['LISTEN_PID'] = str(os.getpid())

    assert listen_fds(False) == [3, 4, 5]
    assert listen_fds(True) == [3, 4, 5]
    assert listen_fds() == []
Пример #4
0
def test_listen_fds():
    os.environ["LISTEN_FDS"] = "3"
    os.environ["LISTEN_PID"] = str(os.getpid())

    assert listen_fds(False) == [3, 4, 5]
    assert listen_fds(True) == [3, 4, 5]
    assert listen_fds() == []
Пример #5
0
def test_listen_fds_no_fds():
    # make sure we have no fds to listen to
    os.unsetenv('LISTEN_FDS')
    os.unsetenv('LISTEN_PID')

    assert listen_fds() == []
    assert listen_fds(True) == []
    assert listen_fds(False) == []
Пример #6
0
    def _get_systemd_socket(self, address):
        fds = sd.listen_fds()
        if not fds:
            return address
        elif len(fds) > 1:
            raise ValueError('Too many listening sockets', fds)

        if isinstance(address, tuple):
            port = address[1]
            # systemd uses IPv6
            if not sd.is_socket_inet(fds[0],
                                     family=socket.AF_INET6,
                                     type=socket.SOCK_STREAM,
                                     listening=True,
                                     port=port):
                raise ValueError("FD {} is not TCP IPv6 socket on port {}",
                                 fds[0], port)
            logger.info('Using systemd socket activation on port %i', port)
            sock = socket.fromfd(fds[0], socket.AF_INET6, socket.SOCK_STREAM)
        else:
            if not sd.is_socket_unix(
                    fds[0], socket.SOCK_STREAM, listening=True, path=address):
                raise ValueError("FD {} is not Unix stream socket on path {}",
                                 fds[0], address)
            logger.info('Using systemd socket activation on path %s', address)
            sock = socket.fromfd(fds[0], socket.AF_UNIX, socket.SOCK_STREAM)

        if sys.version_info[0] < 3:
            # Python 2.7's socket.fromfd() returns _socket.socket
            sock = socket.socket(_sock=sock)
        return sock
Пример #7
0
    def _get_systemd_socket(self, address):
        fds = sd.listen_fds()
        if not fds:
            return address
        elif len(fds) > 1:
            raise ValueError('Too many listening sockets', fds)

        if isinstance(address, tuple):
            port = address[1]
            # systemd uses IPv6
            if not sd.is_socket_inet(fds[0], family=socket.AF_INET6,
                                     type=socket.SOCK_STREAM,
                                     listening=True, port=port):
                raise ValueError("FD {} is not TCP IPv6 socket on port {}",
                                 fds[0], port)
            logger.info('Using systemd socket activation on port %i', port)
            sock = socket.fromfd(fds[0], socket.AF_INET6, socket.SOCK_STREAM)
        else:
            if not sd.is_socket_unix(fds[0], socket.SOCK_STREAM,
                                     listening=True, path=address):
                raise ValueError("FD {} is not Unix stream socket on path {}",
                                 fds[0], address)
            logger.info('Using systemd socket activation on path %s', address)
            sock = socket.fromfd(fds[0], socket.AF_UNIX, socket.SOCK_STREAM)

        if sys.version_info[0] < 3:
            # Python 2.7's socket.fromfd() returns _socket.socket
            sock = socket.socket(_sock=sock)
        return sock
Пример #8
0
def patched_init(self, addr, log_requests=1):
    self.request_queue_size = min(socket.SOMAXCONN, 256)
    SimpleXMLRPCServer.__init__(self,
                                addr,
                                SilenceableXMLRPCRequestHandler,
                                log_requests,
                                bind_and_activate=False)
    self.socket = socket.fromfd(listen_fds()[0], self.address_family,
                                self.socket_type)
Пример #9
0
def map_fds():
    """Return the list of inherited sockets.
    Return null if there arenone.
    """
    fds = {}
    for frozen_fd in frozenset(daemon.listen_fds()):
        journal.send("processing fd: {0}".format(frozen_fd))
        if daemon.is_socket(frozen_fd):
            sock_obj = socket.socket(fileno=frozen_fd)
            journal.send("created socket: name={0}".format(
                sock_obj.getsockname()))
            fds[frozen_fd] = sock_obj
    return fds
Пример #10
0
def listen_fds_with_names():
    '''Tries to get file descriptors from (in order):
    - listen_fds_with_names() # Not yet merged, see https://github.com/systemd/python-systemd/pull/60
    - listen_fds() # With own hack in case it finds $LISTEN_FDNAMES until listen_fds_with_names() is supported
    - None
    In the first two cases, it returns a hash of {fd: name} pairs'''
    try:
        from systemd.daemon import listen_fds_with_names
        # We have the real McCoy
        return listen_fds_with_names()
    except ImportError:
        # Try to fall back to listen_fds(),
        # possbily emulating listen_fds_with_names() here
        try:
            from systemd.daemon import listen_fds
        except ImportError:
            if os.path.exists('/run/systemd/system') and 'LISTEN_FDS' in os.environ:
                logging.error('Software from https://github.com/systemd/python-systemd/ missing; do `apt install python3-systemd` or `pip3 install systemd-python`. Please note the similarly-named `pip3 install python-systemd` does not provide the interfaces needed and may actually need to be UNINSTALLED first!')
                raise
            else:
                logging.info('Please `apt install python3-systemd` for future compatibility')
                return None
        fds = listen_fds()
        if fds:
            listeners = {}
            if 'LISTEN_FDNAMES' in os.environ:
                # Evil hack, should not be here!
                # Is here only because it seems unlikely
                # https://github.com/systemd/python-systemd/pull/60
                # will be merged and distributed anyting soon ;-(.
                # Diverges from original if not enough fdnames are provided
                # (but this should not happen anyway).
                names = os.environ['LISTEN_FDNAMES'].split(':')
            else:
                names = ()
            for i in range(0, len(fds)):
                if i < len(names):
                    listeners[fds[i]] = names[i]
                else:
                    listeners[fds[i]] = 'unknown'
            return listeners
        else:
            return None
Пример #11
0
def get_activation_socket():
    def sock_from_fd(fd):
        if is_socket(fd):
            if is_socket_unix(fd):
                return socket.socket(family=socket.AF_UNIX,
                                     type=socket.SOCK_STREAM,
                                     fileno=fd)
            for fam in [socket.AF_INET6, socket.AF_INET]:
                for t in [socket.SOCK_STREAM]:
                    if is_socket(fd, family=fam, type=t):
                        return socket.socket(family=fam, type=t, fileno=fd)
            return socket.socket(fileno=fd)
        return None

    fds = listen_fds()
    if len(fds) != 1:
        raise Exception("socket activation required")
    sock = sock_from_fd(fds[0])
    if not sock:
        raise Exception("socket activation required")

    return sock
def main(args=None):
	import argparse
	parser = argparse.ArgumentParser(
		usage='%(prog)s [options] [ [--] arguments ]', # argparse fails to build that for $REASONS
		description='Tool to generate and keep tinydns'
			' zone file with dynamic dns entries for remote hosts.')

	parser.add_argument('zone_file', nargs='?',
		help='Path to tinydns zone file with client Ed25519 (base64-encoded)'
				' pubkeys and timestamps in comments before entries.'
			' Basically any line with IPs that has comment in the form of'
				' "dynamic: <ts> <pubkey> <pubkey2> ..." immediately before it (no empty lines'
				' or other comments separating these) can be updated by packet with'
				' proper ts/signature.')

	parser.add_argument('update_command', nargs='*',
		help='Optional command to run on zone file updates and its arguments (if any).'
			' If --uid is specified, all commands will be run after dropping privileges.'
			' Use "--" before it to make sure that none of its args get interpreted by this script.')

	parser.add_argument('-g', '--genkey', action='store_true',
		help='Generate a new random signing/verify'
			' Ed25519 keypair, print both keys to stdout and exit.')

	parser.add_argument('-b', '--bind',
		metavar='[host:]port', default=bytes(default_port),
		help='Host/port to bind listening socket to (default: %(default)s).')
	parser.add_argument('-v', '--ip-af',
		metavar='{ 4 | 6 }', choices=('4', '6'), default=socket.AF_UNSPEC,
		help='Resolve hostname(s) (if any) using specified address family version.'
			' Either "4" or "6", no restriction is appled by default.')

	parser.add_argument('--systemd', action='store_true',
		help='Receive socket fd from systemd, send systemd startup notification.'
			' This allows for systemd socket activation and running the'
				' app from unprivileged uid right from systemd service file.'
			' Requires systemd python bindings.')
	parser.add_argument('-u', '--uid', metavar='uid[:gid]',
		help='User (name) or uid (and optional group/gid) to run as after opening socket.'
			' WARNING: not a proper daemonization - does not do setsid, chdir,'
				' closes fds, sets optional gids, etc - just setresuid/setresgid. Use systemd for that.')

	parser.add_argument('--update-timestamps', action='store_true',
		help='Usually, when no addresses are changed, zone file does not get updated.'
			' This option forces updates to timestamps in addr-block headers.')

	parser.add_argument('-d', '--debug', action='store_true', help='Verbose operation mode.')
	opts = parser.parse_args(sys.argv[1:] if args is None else args)

	global log
	import logging
	logging.basicConfig(level=logging.DEBUG if opts.debug else logging.WARNING)
	log = logging.getLogger()

	if opts.genkey:
		signing_key = key_generate()
		print('Signing key (for client only):\n  ', key_encode(signing_key), '\n')
		print('Verify key (for this script):\n  ', key_encode(key_get_vk(signing_key)), '\n')
		return

	if not opts.zone_file: parser.error('Zone file path must be specified')

	if isinstance(opts.ip_af, types.StringTypes):
		opts.ip_af = {'4': socket.AF_INET, '6': socket.AF_INET6}[opts.ip_af]

	try: host, port = opts.bind.rsplit(':', 1)
	except ValueError: host, port = default_bind, opts.bind
	socktype, port = socket.SOCK_DGRAM, int(port)
	af, addr = get_socket_info(host, port, family=opts.ip_af, socktype=socktype)

	sock = None
	if opts.systemd:
		from systemd import daemon
		try: sock, = daemon.listen_fds()
		except ValueError as err:
			log.info('Unable to get socket from systemd, will create it manually')
		else: sock = socket.fromfd(af, socktype)
		daemon.notify('READY=1')
		daemon.notify('STATUS=Listening for update packets')
	if not sock:
		log.debug('Binding to: %r (port: %s, af: %s, socktype: %s)', addr, port, af, socktype)
		sock = socket.socket(af, socktype)
		sock.bind((addr, port))
	if opts.uid: drop_privileges(opts.uid)

	with open(opts.zone_file, 'rb'): pass # access check
	zone_update_loop(
		opts.zone_file, sock, opts.update_command,
		force_updates=opts.update_timestamps )
Пример #13
0
def main():
    parser = ArgumentParser(
        description="Listens for commands as output by `hades-dhcp-script`.",
        epilog=f"""\
            This server listens on a socket for commands communicating lease events.
            For detailed information about the functionality see `hades-dhcp-script --help`.
            It is the server component for what could have been a single python program,
            however because of performance reasons, it was necessary to circumvent the need
            for a complete python interpreter startup every time such a notification happens.\
        """,
        parents=[common_parser],
    )
    parser.add_argument(
        '--socket',
        nargs='?',
        default=constants.AUTH_DHCP_SCRIPT_SOCKET,
        help=
        f"Socket to listen on. Default: {constants.AUTH_DHCP_SCRIPT_SOCKET}")
    args = parser.parse_args()
    SCRIPT_SOCKET = args.socket
    setup_cli_logging(parser.prog, args)
    try:
        config = load_config(args.config)
    except ConfigError as e:
        print_config_error(e)
        return os.EX_CONFIG
    fds = listen_fds()
    if len(fds) == 0:
        logger.info(
            "Opening UNIX socket at %s.",
            SCRIPT_SOCKET,
        )
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
        try:
            os.unlink(SCRIPT_SOCKET)
        except FileNotFoundError:
            pass
        sock.bind(SCRIPT_SOCKET)
        sock.listen(Server.request_queue_size)
    elif len(fds) == 1:
        logger.info("Using systemd activation socket")
        sock = fds[0]
        if not is_socket_unix(sock, socket.SOCK_STREAM):
            logger.critical(
                "Passed socket is not an AF_UNIX SOCK_STREAM socket")
            return os.EX_USAGE
    else:
        logger.critical(
            "More than one (%d) socket passed via socket activation",
            len(fds),
        )
        return os.EX_USAGE
    engine = db.create_engine(
        config,
        pool_size=1,
        max_overflow=2,
        pool_pre_ping=True,
        pool_reset_on_return='rollback',
    )
    try:
        engine.connect()
    except DBAPIError as e:
        logger.critical("Could not connect to database", exc_info=e)
        return os.EX_TEMPFAIL

    server = Server(sock, engine)
    server.serve_forever()
    return os.EX_OK