1
1
mirror of https://github.com/eblot/pybootd.git synced 2024-08-17 18:00:33 +03:00

Update doc, and use d suffix for local daemons/servers

This commit is contained in:
Emmanuel Blot 2019-09-05 17:44:14 +02:00
parent 6d8a7111cb
commit 3a2148537a
5 changed files with 82 additions and 71 deletions

View File

@ -21,17 +21,15 @@ Requirements
Python
------
- Python_ 2.7 or above is required. Python_ 3.x is not yet supported.
- Six_ compatibility module
- Python_ 3.5+ or above is required. Python_ 2.x is not longer supported.
- Netifaces_ Python module is required on OS X; on Linux only, iproute2_ can be
used as an alternative
- Optional: python-pkg-resources_ Python module
- Optional: python_pkg_resources_ Python module
.. _Python: http://python.org/
.. _Netifaces: http://alastairs-place.net/netifaces/
.. _iproute2: http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2
.. _python-pkg-resources: http://pythonhosted.org/distribute/pkg_resources.html
.. _Six: http://pythonhosted.org/six
.. _python_pkg_resources: http://pythonhosted.org/distribute/pkg_resources.html
Permissions
-----------
@ -81,7 +79,7 @@ Common errors
``pybootd.pxed.BootpError: Unable to detect network configuration``
This error is often triggered when the ``pool_start`` address is not
part of a valid network. Double check the network configuration and
fix up the ``[bootp]`` section so that it matches the actual
fix up the ``[bootpd]`` section so that it matches the actual
network. If you don't want to allocate addresses dynamically from
the pool (with ``pool_count = 0``), you still need to specify
``pool_start`` to some address in the local network you want to
@ -95,8 +93,8 @@ Common errors
Configuration
-------------
``pybootd`` has a few option switches. The server offers two services: bootp
(which supports Dhcp and PXE extensions) and tftp. It is possible to disable
``pybootd`` has a few option switches. The server offers two services: *bootpd*
(which supports DHCP and PXE extensions) and *tftpd*. It is possible to disable
either services.
Usage: pybootd.py [options]
@ -145,8 +143,8 @@ client requests at least an IP address twice:
``file``
The path to the output log file, if ``type`` is set to ``file``.
``[bootp]`` section
...................
``[bootpd]`` section
....................
``access``
Type of access control list. If this option is not defined, all BOOTP
@ -228,36 +226,40 @@ client requests at least an IP address twice:
``servername``
Name of the BOOTP server.
``[mac]`` section
.................
The ``[mac]`` section contains one entry for each MAC address to allow or
block. The value for each entry is a boolean, *i.e.*::
The ``[mac]`` section contains one entry for each MAC address to allow or
block. The value for each entry is a boolean, *i.e.*::
AA-BB-CC-DD-EE-FF = enable
``AA-BB-CC-DD-EE-FF = enable``
Note that due to a limitation of the configuration parser, ':' byte separator
in MAC addresses is not allowed, please use '-' separator.
Note that due to a limitation of the configuration parser, ':' byte separator
in MAC addresses is not allowed, please use '-' separator.
``[static_dhcp]`` section
.........................
The ``[static_dhcp]`` section contains one entry for each MAC
address to associate with a specific IP address. The IP address can be
any IPv4 address in dotted notation, *i.e.*:
The ``[static_dhcp]`` section contains one entry for each MAC
address to associate with a specific IP address. The IP address can be
any IPv4 address in dotted notation, *i.e.*:
AA-BB-CC-DD-EE-FF = 192.168.1.2
``AA-BB-CC-DD-EE-FF = 192.168.1.2``
The MAC addresses specified here will automatically be allowed,
unless ``[mac]`` section specifies otherwise.
The MAC addresses specified here will automatically be allowed,
unless ``[mac]`` section specifies otherwise.
``[uuid]`` section
..................
The ``[uuid]`` section contains one entry for each UUID to allow or block.
The value for each entry is a boolean, *i.e.*::
The ``[uuid]`` section contains one entry for each UUID to allow or block.
The value for each entry is a boolean, *i.e.*::
``xxxxxxxx-aaaa-bbbb-cccc-yyyyyyyyyyyy = enable``
xxxxxxxx-aaaa-bbbb-cccc-yyyyyyyyyyyy = enable
``[http]`` section
..................
@ -281,8 +283,9 @@ The ``pxe``/``dhcp`` option pair enables the remote HTTP server to identify
the boot phase: either a BIOS initialization or an OS boot sequence. When such
differentiation is useless, both options may refer to the same path.
``[tftp]`` section
..................
``[tftpd]`` section
...................
``address``
Address to listen to incoming TFTP requests. When the BOOTP daemon is
@ -310,11 +313,12 @@ differentiation is useless, both options may refer to the same path.
- an absolute path, when the ``root`` option starts with ``/``,
- a URL prefix, to access remote files.
``[filters]`` section
.....................
The ``filters`` section allows on-the-fly pathnames transformation. When a TFTP
client requests some specific filenames, the *tftp* server can translate them
client requests some specific filenames, the *tftpd* server can translate them
to other ones.
This option is useful to serve the very same configuration file (''e.g.''
@ -332,8 +336,8 @@ braces, such as ``{varname}``.
For now, the only supported variable is ``filename``, which is replaced with
the actual requested filename.
The *value* part can also contain a special marker, that tells the *tftp*
daemon to read the replacement pattern from a file. This special marker should
The *value* part can also contain a special marker, that tells the *tftpd*
server to read the replacement pattern from a file. This special marker should
be written with enclosing brackets, such as ``[file]``.
Examples
@ -343,7 +347,7 @@ The following filter::
pxelinux.cfg/* = pybootd/etc/pxe.cfg
tells the *tftp* server that all client requests matching the
tells the *tftpd* server that all client requests matching the
``pxelinux.cfg/*`` pattern should be served the ``pybootd/etc/pxe.cfg`` file
instead. This prevents the client to perform the usual time-costing fallback
requests using UUID, MAC, and suffix addresses before eventually falling
@ -353,7 +357,7 @@ The following filter::
startup = [dir/{filename}.cfg]
tells the *tftp* server that when the ``startup`` file is requested, it should
tells the *tftpd* server that when the ``startup`` file is requested, it should
read out the actual filename from the ``dir/startup.cfg`` file.
HTTP-based authentication
@ -382,15 +386,16 @@ this feature. It can be found within the ``tests/`` subdirectory. See the
``config.ini`` file for this test daemon. The test daemon expects the ``pxe``
path to be set to ``/boot`` and the ``dhcp`` path to ``/linux``.
Sample configurations
~~~~~~~~~~~~~~~~~~~~~
Installing a Debian 6.0 machine from the official archive
---------------------------------------------------------
As the *tftp* daemon is able to retrieve remote files using the HTTP protocol,
there is no need to manually download any file from a Debian mirror. The daemon
will forward all file requests to the mirror on behalf of the client being
installed.
As pybootd's *tftpd* server is able to retrieve remote files using the HTTP
protocol, there is no need to manually download any file from a Debian mirror.
The daemon will forward all file requests to the mirror on behalf of the client
being installed.
The ``pybootd.ini`` would contain::
@ -400,7 +405,7 @@ The ``pybootd.ini`` would contain::
; show informative and error messages only (disable verbose mode)
level = info
[bootp]
[bootpd]
; do not force a full PXE boot-up cycle to accept the client
allow_simple_dhcp = enable
; First BOOTP/DHCP address to generate
@ -410,7 +415,7 @@ The ``pybootd.ini`` would contain::
; boot-up executable the client should request through TFTP
boot_file = pxelinux.0
[tftp]
[tftpd]
; URL to install a Debian 6.0 Intel/AMD 64-bit network installation
root = http://http.us.debian.org/debian/dists/squeeze/main/installer-amd64/current/images/netboot

View File

@ -31,7 +31,9 @@ from .pxed import BootpServer
from .tftpd import TftpServer
from .util import logger_factory, EasyConfigParser
#pybootd: disable-msg=broad-except
#pylint: disable-msg=broad-except
#pylint: disable-msg=missing-docstring
#pylint: disable-msg=invalid-name
class BootpDaemon(Thread):

View File

@ -2,11 +2,10 @@
type = stderr
level = info
[bootp]
[bootpd]
address = 0.0.0.0
; pool_start should be in a valid subnet
; pool_start = 192.168.25.100
pool_start = 10.113.116.245
pool_start = 192.168.25.100
pool_count = 5
domain = localdomain
server_name = debug
@ -14,13 +13,14 @@ boot_file = pxelinux.0
lease_time = 86400
access = mac
allow_simple_dhcp = enable
dns = 10.130.0.2
dns = 8.8.8.8
set_gateway = true
; use "nc -l -u 127.0.0.1 -p 12345" to debug
; notify = 192.168.26.201:12345;192.168.26.200:12345
[mac]
00-1E-4F-C4-95-EE = enable
; see doc: byte separator should be defined with dash, not column
00-AA-55-12-34-56 = enable
[uuid]
12345678-abcd-ef00-1111-abcdefabcdef = enable
@ -32,10 +32,10 @@ pxe = boot
dhcp = linux
always_check = disable
[tftp]
[tftpd]
;address = (use address from bootpd)
;root = ./images
root = http://http.us.debian.org/debian/dists/squeeze/main/installer-amd64/current/images/netboot
root = ./images
;root = http://http.us.debian.org/debian/dists/squeeze/main/installer-amd64/current/images/netboot
[filters]
;pxelinux.cfg/* = pybootd/etc/pxe.cfg

View File

@ -182,6 +182,8 @@ class BootpServer:
ACCESS_REMOTE = ['http'] # Access modes, remotely retrieved
(ST_IDLE, ST_PXE, ST_DHCP) = range(3) # Current state
BOOTP_SECTION = 'bootpd'
def __init__(self, logger, config):
self.sock = []
self.log = logger
@ -190,16 +192,15 @@ class BootpServer:
self.ippool = {} # key MAC address string, value assigned IP string
self.filepool = {} # key IP string, value pathname
self.states = {} # key MAC address string, value client state
self.bootp_section = 'bootp'
self.pool_start = self.config.get(self.bootp_section, 'pool_start')
self.pool_start = self.config.get(self.BOOTP_SECTION, 'pool_start')
if not self.pool_start:
raise BootpError('Missing pool_start definition')
self.pool_count = int(self.config.get(self.bootp_section,
self.pool_count = int(self.config.get(self.BOOTP_SECTION,
'pool_count', '10'))
self.netconfig = get_iface_config(self.pool_start)
if not self.netconfig:
host = self.config.get(self.bootp_section, 'address', '0.0.0.0')
host = self.config.get(self.BOOTP_SECTION, 'address', '0.0.0.0')
self.netconfig = get_iface_config(host)
if not self.netconfig:
# the available networks on the host may not match the config...
@ -208,7 +209,7 @@ class BootpServer:
keys = sorted(self.netconfig.keys())
self.log.info('Using %s' % ', '.join(map(
':'.join, zip(keys, [self.netconfig[k] for k in keys]))))
nlist = self.config.get(self.bootp_section, 'notify')
nlist = self.config.get(self.BOOTP_SECTION, 'notify')
self.notify = []
if nlist:
try:
@ -218,7 +219,7 @@ class BootpServer:
self.notify.append((n[0], int(n[1])))
except Exception as exc:
raise BootpError('Invalid notification URL: %s' % exc)
access = self.config.get(self.bootp_section, 'access')
access = self.config.get(self.BOOTP_SECTION, 'access')
if not access:
self.acl = None
else:
@ -257,8 +258,8 @@ class BootpServer:
return self.netconfig
def bind(self):
host = self.config.get(self.bootp_section, 'address', '0.0.0.0')
port = self.config.get(self.bootp_section, 'port',
host = self.config.get(self.BOOTP_SECTION, 'address', '0.0.0.0')
port = self.config.get(self.BOOTP_SECTION, 'port',
str(BOOTP_PORT_REQUEST))
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
sock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
@ -398,8 +399,8 @@ class BootpServer:
if newstate == self.ST_IDLE:
sdhcp = 'allow_simple_dhcp'
simple_dhcp = \
self.config.has_option(self.bootp_section, sdhcp) and \
to_bool(self.config.get(self.bootp_section, sdhcp))
self.config.has_option(self.BOOTP_SECTION, sdhcp) and \
to_bool(self.config.get(self.BOOTP_SECTION, sdhcp))
if not simple_dhcp:
self.log.info('Request from %s ignored (idle state)' % mac_str)
return
@ -497,7 +498,7 @@ class BootpServer:
raise BootpError('No more IP available in definined pool')
mask = iptoint(self.config.get(
self.bootp_section, 'netmask', self.netconfig['mask']))
self.BOOTP_SECTION, 'netmask', self.netconfig['mask']))
reply_broadcast = iptoint(ip) & mask
reply_broadcast |= (~mask) & ((1 << 32)-1)
buf[BOOTP_YIADDR] = inet_aton(ip)
@ -517,12 +518,12 @@ class BootpServer:
buf[BOOTP_SIADDR] = inet_aton(server_addr)
# sname
buf[BOOTP_SNAME] = \
'.'.join([self.config.get(self.bootp_section,
'.'.join([self.config.get(self.BOOTP_SECTION,
'servername', 'unknown'),
self.config.get(self.bootp_section,
self.config.get(self.BOOTP_SECTION,
'domain', 'localdomain')]).encode()
# file
buf[BOOTP_FILE] = self.config.get(self.bootp_section,
buf[BOOTP_FILE] = self.config.get(self.BOOTP_SECTION,
'boot_file', '\x00').encode()
if not dhcp_msg_type:
@ -573,18 +574,18 @@ class BootpServer:
pkt += spack('!BB4s', DHCP_SERVER, 4, server)
mask = inet_aton(self.config.get(
self.bootp_section, 'netmask', self.netconfig['mask']))
self.BOOTP_SECTION, 'netmask', self.netconfig['mask']))
pkt += spack('!BB4s', DHCP_IP_MASK, 4, mask)
gateway_addr = self.config.get(self.bootp_section, 'gateway', '')
gateway_addr = self.config.get(self.BOOTP_SECTION, 'gateway', '')
if gateway_addr:
gateway = inet_aton(gateway_addr)
else:
gateway = server
pkt += spack('!BB4s', DHCP_IP_GATEWAY, 4, gateway)
dns = self.config.get(self.bootp_section,
dns = self.config.get(self.BOOTP_SECTION,
'dns', None)
if dns:
if dns.lower() == 'auto':
@ -595,7 +596,7 @@ class BootpServer:
dns_ip = inet_aton(dns_str)
pkt += spack('!BB4s', DHCP_IP_DNS, 4, dns_ip)
pkt += spack('!BBI', DHCP_LEASE_TIME, 4,
int(self.config.get(self.bootp_section,
int(self.config.get(self.BOOTP_SECTION,
'lease_time',
str(24*3600))))
pkt += spack('!BB', DHCP_END, 0)

View File

@ -404,25 +404,28 @@ class TftpServer:
Each request is handled in its own thread
"""
TFTP_SECTION = 'tftpd'
def __init__(self, logger, config, bootpd=None):
self.log = logger
self.config = config
self.sock = []
self.bootpd = bootpd
self.blocksize = int(self.config.get('tftp', 'blocksize', '512'))
self.timeout = float(self.config.get('tftp', 'timeout', '2.0'))
self.retry = int(self.config.get('tftp', 'blocksize', '5'))
self.root = self.config.get('tftp', 'root', os.getcwd())
self.blocksize = int(self.config.get(self.TFTP_SECTION, 'blocksize',
'512'))
self.timeout = float(self.config.get(self.TFTP_SECTION, 'timeout', '2.0'))
self.retry = int(self.config.get(self.TFTP_SECTION, 'blocksize', '5'))
self.root = self.config.get(self.TFTP_SECTION, 'root', os.getcwd())
self.fcre, self.filepatterns = self.get_file_filters()
self.genfilecre = recompile(r'\[(?P<name>[\w\.\-]+)\]')
def bind(self):
netconfig = self.bootpd and self.bootpd.get_netconfig()
host = self.config.get('tftp', 'address',
host = self.config.get(self.TFTP_SECTION, 'address',
netconfig and netconfig['server'])
if not host:
raise TftpError(TftpError.NO_SUCH_USER, 'TFTP address no defined')
port = int(self.config.get('tftp', 'port', str(TFTP_PORT)))
port = int(self.config.get(self.TFTP_SECTION, 'port', str(TFTP_PORT)))
sock = socket(AF_INET, SOCK_DGRAM)
self.sock.append(sock)
sock.bind((host, port))
@ -433,7 +436,7 @@ class TftpServer:
if not self.bootpd.is_alive():
self.log.info('Bootp daemon is dead, exiting')
break
r, w, e = select(self.sock, [], self.sock)
r = select(self.sock, [], self.sock)[0]
for sock in r:
data, addr = sock.recvfrom(516)
tc = TftpConnection(self)