Skip to content

Commit

Permalink
feat: introduce bundled TFTP server support and improve docs
Browse files Browse the repository at this point in the history
  • Loading branch information
alexculea committed May 9, 2024
1 parent e2bfc47 commit ba5c6ef
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 42 deletions.
9 changes: 5 additions & 4 deletions .env
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
PREBOOT_OXIDE_SERVER_IPV4=<your-ip>
PXE_DHCP_BOOT_FILE=/path/to/the/bootable/image
PO_LOG_LEVEL=Preboot_Oxide=error
PO_IFACES=<iface1>,<iface2>
PO_TFTP_SERVER_IPV4=<your-tftp-server-ipv4>
PO_BOOT_FILE=<your-boot-file-path>
PO_LOG_LEVEL=Preboot_Oxide=trace
PO_IFACES=<your-interfaces>
PO_TFTP_SERVER_DIR_PATH=<your-tftp-server-dir-path>
4 changes: 0 additions & 4 deletions .env.dev

This file was deleted.

63 changes: 63 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ license = "AGPL-3.0-only"
[dependencies]
anyhow = "1.0.79"
async-std = "1.12.0"
async-tftp = "0.3.6"
dhcproto = "0.11.0"
dotenv = "0.15.0"
env_logger = "0.10.1"
Expand Down
47 changes: 33 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
# Simple DHCP Server for Preboot Execution Environment (PXE) booting
# Preboot-Oxide: simple network boot server (PXE)

<img src="assets/logo.webp" height="96" style="border-radius: 96px" />

PXE boot server for networks where a DHCP server already exists and should be kept separate.
This tool currently takes care of the DHCP part of the PXE network [booting process](https://en.wikipedia.org/wiki/Preboot_Execution_Environment]). It does not handle the TFTP service, a separate tool is required for TFTP.
This tool takes care of both the DHCP and TFTP of the PXE network [booting process](https://en.wikipedia.org/wiki/Preboot_Execution_Environment]).

## Use cases
- Quickly boot OSes from the network without having to use USB drives
- Automate OS installs for a large number of devices
- Integrate OS installs into Infrastructure as Code paradigms*

Highlights include:
- facilitates easy & simple setup to get network booting working
- compatible with **an existing authoritative DHCP server** on the same network
- excellent maintenability and memory safety due to being written in Rust
- Facilitates easy & simple setup to get network booting working
- Compatible with **an existing authoritative DHCP server** on the same network
- Excellent maintenability and memory safety due to being written in Rust

## Supported platforms
Should compile & work on all Unix derivatives - only tested on Debian Linux.

## Project Status
Early stage - not recommended for critical or large scale use. The tool was was tested to work with limited device/network setup permutations.
Early stage - not recommended for critical or large scale use. Was was tested to work with limited device/network setup permutations.

### Known issues or limitations
- No IPv6 support
- Requires elevated access to listen on privileged ports 67 & 68
- Requires elevated access to listen on privileged ports 67, 68 and 69
- Requires a separate DHCP server as it is designed specifically for PXE boots and not for *usual* DHCP operation
- It will conflict with DHCP servers and clients on the same machine**
- Lacks OS service configuration
- There's a DOS vulnerability where a flood of DHCP `OFFER` without follow up `ACK` will cause the process to fill the system memory.

Expand All @@ -44,22 +46,30 @@ Configuration supports either process environment variables or using a dotenv (.

```BASH
# using variables
PO_SERVER_IPV4=<your-ip> PO_BOOT_FILE=/path/to/the/bootable/image ./target/release/Preboot-Oxide
PO_TFTP_SERVER_DIR_PATH=/dir/hosting/the/boot/files PO_BOOT_FILE=/path/to/the/bootable/image ./target/release/Preboot-Oxide
```

Using .env file:
```BASH
# configure the .env file first
content="
PO_SERVER_IPV4=<your-ip>
PO_BOOT_FILE=/path/to/the/bootable/image
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
```

PXE network booting should now work. ❇️

(see Requirements & Errors otherwise)

### Requirements
- The booting device should support the (usually very common) PXE boot procedure.
- Both the server and the booting device need to be on the same LAN, or within a network where UDP broadcast is supported (this generally doesn't work outside local networks).

### Errors

1. `Address already in use (os error 98)`: Preboot-Oxide requires both ports 67 and 68 to be free which are usually not. On most Linux systems `dhclient` will occupy port 68 and if a DHCP server is installed that will occupy 67.
Expand All @@ -73,14 +83,20 @@ echo "$content" > ./target/release/.env

# temporary operation with these processes stopped should be fine for a while but will break the OS DHCP operation of configuring its network interfaces

# to get around this, it is possible to setup an UDP duplicate proxy using iptables and ensure the DHCP packets get delivered to both dhclient/dhcpd and Preboot-Oxide by setting up a virtual network interface for each and routing the packets accordingly. support for this type of proxying is in on the roadmap

# additionally, static network configuration can be used to not need the use of `dhclient`.
# to get around this permanently, static network configuration can be used to not need the use of `dhclient`.
```

### Logging

For a quick start on seeing what is wrong within the logs just re-rerun with the logging set to `info` as seen below.
```BASH
PO_LOG_LEVEL=info PO_TFTP_SERVER_DIR_PATH=/dir/hosting/the/boot/files PO_BOOT_FILE=/path/to/the/bootable/image ./target/release/Preboot-Oxide
# optionally, change 'info' to 'debug' or 'trace' for more verbose logging.
```

### Supported ENV variables

- `PO_SERVER_IPV4`: IPv4 of the running & TFTP boot server. Optional, if not specified, it's assumed that the TFTP server is at the same address as Preboot-Oxide.
- `PO_TFTP_SERVER_IPV4`: IPv4 of the running & TFTP boot server. Optional, if not specified, the server will start a TFTP service and will indicate its own IP to the booting clients to reach the service. If this has a value, the TFTP service is disabled.
- `PO_BOOT_FILE`: Path to the boot image on the TFTP server. Relative to your TFTP server, often seen as `pxelinux.0` or `folder/path/to-it.efi`. Parameter is required.
- `PO_LOG_LEVEL`

Expand All @@ -101,4 +117,7 @@ sudo su
lldb-server platform --server --listen 127.0.0.1:12345
```

## Footnotes
*: Using unattended install setups it is possible to customize any aspect of the OS install such that all installs are reproducible.

**: As both the OS DHCP client and Preboot-Oxide need port 68, the client operation has to be interrupted to start the boot server. This should be fine in most scenarios if the network was already configured and the DHCP client can be stopped safely - however long term effects could include network disconnection if the network configuration is changed while the DHCP client is off or if the boot server was configured as a service, it could interfere in raising up the networks due to an erroring client. One possible workaround is to ensure that the network is up first, then stop the client and then start the server. This should allow both functions normally for the duration of the IP lease. A fully compatible solution is still being researched for Debian, to submit a proposal please don't hesitate to open an issue.
3 changes: 1 addition & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# Roadmap

Unorder list of features planned:
Unordered list of features planned:

- Mitigate DOS vulnerabilities by ensuring sessions are limited in number and periodically removing old session entries
- Add support for serving TFTP as well
- Add support for proxying DHCP traffic to `dhclient` on Linux based distros
- Add service configuration template file for `systemd`
- Add E2E documentation on setting up a real OS to boot
Expand Down
11 changes: 9 additions & 2 deletions src/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub struct Conf {
boot_file: Option<String>,
boot_server_ipv4: Option<Ipv4Addr>,
ifaces: Option<Vec<String>>,
tftp_path: Option<String>
}

pub const ENV_VAR_PREFIX: &str = "PO_";
Expand All @@ -24,17 +25,19 @@ impl Conf {

pub fn from_proccess_env() -> Result<Self> {
let boot_server_ipv4: Option<Ipv4Addr> =
std::env::var(format!("{ENV_VAR_PREFIX}SERVER_IPV4"))
std::env::var(format!("{ENV_VAR_PREFIX}TFTP_SERVER_IPV4"))
.unwrap_or_default()
.parse()
.ok();
let boot_file = std::env::var(format!("{ENV_VAR_PREFIX}BOOT_FILE")).ok();
let tftp_path = std::env::var(format!("{ENV_VAR_PREFIX}TFTP_SERVER_DIR_PATH")).ok();
let ifaces_csv = std::env::var(format!("{ENV_VAR_PREFIX}IFACES")).ok();
let ifaces = ifaces_csv.map(|csv| csv.split(",").map(|s| s.to_string()).collect());

Ok(Self {
boot_server_ipv4,
boot_file,
boot_file,
tftp_path,
ifaces,
})
}
Expand All @@ -54,4 +57,8 @@ impl Conf {
pub fn get_ifaces(&self) -> Option<&Vec<String>> {
self.ifaces.as_ref()
}

pub fn get_tftp_path(&self) -> Option<String> {
self.tftp_path.clone()
}
}
Loading

0 comments on commit ba5c6ef

Please sign in to comment.