Skip to content

Latest commit

 

History

History
367 lines (278 loc) · 15.8 KB

configuring.md

File metadata and controls

367 lines (278 loc) · 15.8 KB

Configuration

Configuration is supported by either process ENV variables or with YAML config file. For very quick & simple setups ENV vars might be sufficient, YAML otherwise.

Process environment variables

If a YAML file is found, all process env variables are ignored.

Supported ENV variables

  • PO_TFTP_SERVER_DIR_PATH: Path to the local directory to be served by the TFTP service. Optional and if not supplied, it is expected that PO_TFTP_SERVER_IPV4 will be given instead to use an external TFTP server.

    When this option is configured, it activates the TFTP service at port 69 on the configured PO_IFACES and the specified directory in read only mode. No remote changes are allowed but ⚠️ the directory becomes accessible to any client connecting ⚠️. TFTP doesn't support authentication.

  • PO_TFTP_SERVER_IPV4: IPv4 of the running Trivial File Transfer Protocol (TFTP) boot server. Only necessary when using a separate TFTP server. If not specified, a TFTP service will be started, indicating its own IP to the booting clients to reach the service. If this has a value, the TFTP service is disabled.

  • PO_BOOT_FILE: The UNIX path to the file to be executed at boot time from within the TFTP service. The boot file path is relative to the directory of the TFTP service. If the file is at /tmp/boot/file.bin on the local disk and the TFTP service is configured to serve from /tmp/boot then the boot_file specified should be just file.bin. Parameter is required.

  • PO_LOG_LEVEL: Filter and verbosity level for output to stdout.

    Syntax: <component>=level.

    Example: PO_LOG_LEVEL=preboot_oxide=info

    Allows setting log level to Preboot Oxide and its dependencies. The supported levels are: error, warn, info, debug, trace.

    Example: PO_LOG_LEVEL=preboot_oxide=info, for even more verbose output: PO_LOG_LEVEL=trace.

    Default: error.

  • PO_IFACES: Comma separated names of the network interfaces the program should listen on. Example: PO_IFACES=enp0s3,enp0s8. Optional, unless specified, it will listen on all network interfaces.

  • PO_CONF_PATH: Path for overriding the default YAML configuration file.

  • PO_MAX_SESSIONS: Optional number of maximum concurrent sessions to be allowed. Defaults to 500, used to protect against flood filling the system memory.

Specifying ENV variables can be achieved in a number of ways depending on the OS and how the executable is ran. Some examples:

👉 Simple CLI invocation:

PO_TFTP_SERVER_DIR_PATH=/dir/hosting/the/boot/files PO_BOOT_FILE=/path/to/the/bootable/image preboot-oxide

👉 .env file in the same directory as the executable

content="
  PO_TFTP_SERVER_DIR_PATH=/dir/hosting/the/boot/files
  PO_BOOT_FILE=/the/bootable/image
"
echo "$content" > ./target/release/.env

# and run
./target/release/preboot-oxide

👉 OS-level, useful when running as a service, it will depend on the OS used - for most Linux distros it is possible by adding the variables to the /etc/environment file

YAML configuration file

In short, YAML is a static configuration format similar to JSON that uses tabs instead of braces ({}). Simple tutorial.

The YAML content is loaded from PO_CONF_PATH env variable or from ~/.config/preboot-oxide/preboot-oxide.yaml. There is a sample file at the project root here, reading on is recommended for a better understanding.

This .yaml file config will override process ENV variables. When running as service with systemd, the location will correspond to the root user at /root/.config/preboot-oxide/preboot-oxide.yaml. It is possible to override the path using the PO_CONF_PATH env variable. This SO answer describes how to set env variables for systemd services.

Conceptually, all PXE booting devices require only two parameters. The path of the executable file to run at boot time and where to get that file from. The first is a Unix style path, the 2nd is an IPv4 address where the Trivial File Transfer Protocol (TFTP) service is available to serve the file.

The configuration is split between global vs client specific sections. The global section applies to the boot server generally, such as what network cards to use or where are the files for booting. The client sections define the boot file and the TFTP IP. Here are a few examples:

Simple, same boot for all clients on the network

tftp_server_dir: /where/the/boot/files/are
# if given, the TFTP service will be started and serve this path
# if not given, it is expected that a boot_server_ipv4 is given instead

# this section defines the boot file and server to be used by all clients
default:
  boot_file: /the/boot/file.efi

# that's it, place this in ~/.config/preboot-oxide/preboot-oxide.yaml

Per-device config from its MAC address

tftp_server_dir: /where/the/boot/files/are
match:
  # device #1
  - select:
      ClientMacAddress: 08:00:27:E7:DE:FE # this can also be lowercase
    conf:
      boot_file: /path/to/bootfile.efi

  # device #2
  - select:
      ClientMacAddress: 01:02:03:04:05:06
    conf:
      boot_file: /other/path/to/bootfile.efi

# any other client will be ignored

A special client or the default for the rest

tftp_server_dir: /where/the/boot/files/are
default:
  # by default we will use this
  boot_file: /path/for/all/clients.bin 
  
match:
  # however for this client only
  - select:
      ClientMacAddress: 08:00:27:E7:DE:FE 

    # is given this
    conf:
      boot_file: /path/to/bootfile.efi 
      boot_server_ipv4: 1.2.3.4

When overriding for a specific client, we can specify either boot_file or the boot_server_ipv4 or both. If any is not given, the ones in default will be used. Thus in the example above, client 08:00:27:E7:DE:FE will be told to contact server 1.2.3.4 via TFTP and use /path/to/bootfile.efi instead.

By CPU architecture

tftp_server_dir: /where/the/boot/files/are

match:
  # UEFI aarch64 (ARM)
  - select:
      ClassIdentifier: Arch:00011
    regex: true # the value above is treated as a regular expression
    conf:
      boot_file: /path/to/arm64/uefi.efi
  # UEFI amd64 or x86 
  - select:
      ClassIdentifier: Arch:0000[6-7]
    regex: true 
    conf:
      boot_file: /path/to/uefi.efi

The list of standardized archtectures is here.

Using an external TFTP server

# tftp_server_dir: /where/the/boot/files/are
# commenting this out will disable TFTP service

default:
  boot_file: /path/to/bootfile.efi
  boot_server_ipv4: 12.34.56.78 # for all clients

match:
  - select:
      ClientMacAddress: 08:00:27:E7:DE:FE 
    conf:
      boot_file: /path/to/bootfile.efi 
      boot_server_ipv4: 123.45.67.89 # different TFTP for this client

or just override the boot file but use the same TFTP server

default:
  boot_file: /path/to/x86-bios.bin
  boot_server_ipv4: 12.34.56.78

match:
  - select:
      ClientMacAddress: 00:01:02:03:04:05
    conf:
    boot_file: /path/to/x86-uefi.efi # this client boots UEFI

Only use certain networks

ifaces:
  - enp0s3
  - enp0s8

default:
  boot_file: /path/for/all/clients.bin
  boot_server_ipv4: 12.34.56.78

Reference

  • ifaces: List of network interfaces for listening. Ex:

    ifaces:
      - enp0s3
      - enp0s8
  • tftp_server_dir: Path to the local directory to be served by the TFTP service.

    tftp_server_dir: /where/the/boot/files/are

    Optional, if not given, it is expected that boot_server_ipv4 will be given instead to use an external TFTP server.

    When this option is configured, it activates the TFTP service at port 69 on the configured ifaces and the specified directory in read only mode. No remote changes are allowed but ⚠️ the directory becomes accessible to any client connecting ⚠️. TFTP doesn't support authentication.

  • boot_file: The UNIX path to the file to be executed at boot time from within the TFTP service. The boot file path is relative to the directory of the TFTP service. If the file is at /tmp/boot/file.bin on the local disk and the TFTP service is configured to serve from /tmp/boot then the boot_file specified should be just file.bin.

  • boot_server_ipv4: IPv4 address of TFTP service, for when it is desirable to use an external TFTP service. If not specified, a TFTP service will be started, serving files from the specified tftp_server_dir.

  • default: Holds the boot_file and, optionally, boot_server_ipv4 to provide to the booting client devices. boot_server_ipv4.

  • max_sessions: Optional, defaults to 500. Represents the maximum number of allowed sessions at the same time. A session starts when an OFFER message is seen from DHCP to the booting client and ends when either the client ACKed or refused the request. Sessions older than 3 minutes are automatically removed. This is used to prevent filling the system memory in case of a flood of DHCP messages on the network.

  • match: List of entries to match, optional. Subfields:

    • select: List of fields and values to match. Unless regex is true, the matching is done by value, case insensitive.

      • Supported fields:

        ClientMacAddress
        ClassIdentifier
        HardwareType
        ClientSystemArchitecture
        RequestedIpAddress
        ServerIdentifier
        
      • Example:

        match:
        - select:
            ClassIdentifier: PXEClient:Arch:00007:UNDI:003000
            ClientMacAddress: 08:00:27:be:d8:91
            HardwareType: eth
            ...etc
          conf:
            boot_file: debian-installer/amd64/bootnetx64.efi
    • regex: true or false. When true, the value of the select field will be interpreted as a regular expression. The engine used can be tested with https://regex101.com/ (select Rust from the Flavor on the left).

    • conf: The resulting config when the client matched the select. Subfields:

      • boot_file: Same as above. If not specified, the boot_file in the default section will be used
      • boot_server_ipv4: Same as above. If not specified the boot_server_ipv4 will be used. If default doesn't specify a boot_server_ipv4 either, it is expected to set a path in tftp_server_dir and clients will be instructed to use the included TFTP service.
    • match_type: all or any. For any, if any of the select field-values match, the entry is considered a match. For all, all field-values in select have to match. In both cases, the first matching entry in the order of definition is used, thus it is best to declare the more specific matches first.

Troubleshooting config issues

The best way to troubleshoot configuration issues is to inspect the output of preboot-oxide. By default only hard errors are printed however we can activate the trace log level to get a view on the internal logic of the booting process. This is possible by starting preboot-oxide with the PO_LOG_LEVEL environment variable. Depending on how it was installed, here are the two options:

When running as a service with systemd

We can add ENV variables to systemd processes by:

sudo systemctl edit preboot-oxide

The above should open an editor to the override.conf for the service. In the file we add the environment definition:

[Service]
Environment="PO_LOG_LEVEL=preboot_oxide::conf=trace"

We should end up with something like this:

### Editing /etc/systemd/system/preboot-oxide.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file

[Service]
Environment="PO_LOG_LEVEL=preboot_oxide::conf=trace"

### Lines below this comment will be discarded

### /lib/systemd/system/preboot-oxide.service
# [Unit]
# Description=PXE Boot server
# After=network.target
# Wants=
# 
# [Service]
# Restart=always
# Type=simple
# ExecStart=/bin/preboot-oxide
# Environment=
# 
# [Install]
# WantedBy=multi-user.target

Save the file, restart the service and open the logs in follow mode:

sudo systemctl restart preboot-oxide
sudo journalctl -f -u preboot-oxide # starts the logging in -f follow mode

Now that output logs are in trace mode and they are followed, the network boot from the client device can be started which should result like those in Example trace logs.

It is possible to see what the clients are reporting this way and configure accordingly.

When running without systemd

The preboot-oxide process should be started with PO_LOG_LEVEL environment variable having the value preboot_oxide::conf=trace. Check the OS/shell manual on how to specify environment variables to processes.

Once the started, the process will output logs as seen in Example trace logs.

Example trace logs

When a match was found:

 2024-06-06T14:56:54.454Z TRACE preboot_oxide::conf > Matching regex field ClassIdentifier="PXEClient:Arch:00007:UNDI:003000" to "Arch:00007", matching = true
 2024-06-06T14:56:54.454Z TRACE preboot_oxide::conf > Found matching entry from 'match' rule.
ConfEntry {
    boot_file: Some(
        "/this/specific/client",
    ),
    boot_server_ipv4: None,
}
 2024-06-06T14:56:54.454Z TRACE preboot_oxide::conf > Final result combined with default:
ConfEntry {
    boot_file: Some(
        "/this/specific/client",
    ),
    boot_server_ipv4: None,
}
 2024-06-06T14:56:58.071Z TRACE preboot_oxide::conf > Matching regex field ClassIdentifier="PXEClient:Arch:00007:UNDI:003000" to "Arch:00007", matching = true
 2024-06-06T14:56:58.071Z TRACE preboot_oxide::conf > Found matching entry from 'match' rule.

When a match was not found

 2024-06-06T15:36:21.363Z TRACE preboot_oxide::conf > Matching regex field ClassIdentifier="PXEClient:Arch:00007:UNDI:003000" to "Arch:00009", matching = false
 2024-06-06T15:36:21.363Z TRACE preboot_oxide::conf > No matching entry found from 'match' rule.